﻿// GL01View.cpp : implementation of the CGL01View class
//

#include "stdafx.h"
#include "GL01.h"

#include "GL01Doc.h"
#include "GL01View.h"

#include "gl/gl.h"
#include "gl/glu.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define MAXELEMENTS         100
#define MAXNODESPERELEMENT  4

// Display List Structure
typedef struct DisplayList {
    float   R, G, B;
    int     nNodes;
    float   *XPos, *YPos;
} DisplayList;

DisplayList DList[MAXELEMENTS];
float       XNodes[MAXELEMENTS*MAXNODESPERELEMENT], YNodes[MAXELEMENTS*MAXNODESPERELEMENT];
int         nElements = 0;
CPoint      anchor, last; // Mouse location points
float       wsx, wsy, wcx, wcy, scale, vsx, vsy; // Present Window Info
float       old_wsx, old_wsy, old_wcx, old_wcy; // Initial Window Info

/////////////////////////////////////////////////////////////////////////////
// CGL01View

IMPLEMENT_DYNCREATE(CGL01View, CView)

BEGIN_MESSAGE_MAP(CGL01View, CView)
    //{{AFX_MSG_MAP(CGL01View)
    ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
    ON_COMMAND(ID_VIEW_REDRAW, OnViewRedraw)
    ON_UPDATE_COMMAND_UI(ID_VIEW_REDRAW, OnUpdateViewRedraw)
    ON_WM_SIZE()
    ON_WM_CREATE()
    ON_WM_DESTROY()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
    ON_WM_MOUSEMOVE()
    ON_WM_ERASEBKGND()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGL01View construction/destruction

CGL01View::CGL01View()
{
    // TODO: add construction code here

}

CGL01View::~CGL01View()
{
}

BOOL CGL01View::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
    //  the CREATESTRUCT cs

    cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    return CView::PreCreateWindow(cs);
}

/////////////////////////////////////////////////////////////////////////////
// CGL01View drawing

void CGL01View::OnDraw(CDC* pDC)
{
    CGL01Doc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    // TODO: add draw code for native data here
    DrawScene();						// draw initial OpenGL scene
}

void CGL01View::DrawScene()
{
	// initialize OpenGL window scene
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // COLOR RGBA(0, 0, 0, 0) -> Black
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0f, 1.0f, 1.0f);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    if (nElements > 0) DrawList();
    glFlush();
}

void CGL01View::DrawAxes()
{
    float   xv, yv;

    glColor3f(1.0f, 1.0f, 1.0f);
    glBegin(GL_LINES);
        Win2VPortMapping(&xv, &yv, 60.0f/scale, 0.0f);
        glVertex2f(xv, yv);
        Win2VPortMapping(&xv, &yv, 0.0f, 0.0f);
        glVertex2f(xv, yv);
        glVertex2f(xv, yv);
        Win2VPortMapping(&xv, &yv, 0.0f, 60.0f/scale);
        glVertex2f(xv, yv);
    glEnd();

    // Write text on the GL window
    wglUseFontBitmaps(m_hDC, 0, 256, 1000);
    glListBase(1000);
    Win2VPortMapping(&xv, &yv, 70.0f/scale, -5.0f/scale);
    glRasterPos2f(xv, yv);
    glCallLists(1, GL_UNSIGNED_BYTE, _T("x"));
    Win2VPortMapping(&xv, &yv, -5.0f/scale, 70.0f/scale);
    glRasterPos2f(xv, yv);
    glCallLists(1, GL_UNSIGNED_BYTE, _T("y"));
    glFlush();
    glDeleteLists(1000, 256) ;
}

void CGL01View::DrawList()
{
    int i, j;
    float xv, yv, *xpos, *ypos;

    DrawAxes ();

    for (i = 0; i < nElements; i++) {
        glColor3f (DList[i].R, DList[i].G, DList[i].B);
        glBegin (GL_LINE_LOOP);
            xpos = DList[i].XPos;
            ypos = DList[i].YPos;
            for (j = 0; j < DList[i].nNodes; j++) {
                Win2VPortMapping (&xv, &yv, *(xpos++), *(ypos++));
                glVertex2f (xv, yv);
            }
        glEnd ();
    }
}

void CGL01View::Win2VPortMapping(float *xv, float *yv, float xw, float yw)
{
    // scale = min {vsx/wsx, vsy/wsy}
    // clipping volumn - leftTop (-1, 1), rightBottom (1, -1)
    *xv = (xw - wcx) * scale / vsx;
    *yv = (yw - wcy) * scale / vsy;
}

void CGL01View::Mouse2WinMapping(float *xw, float *yw, float xv, float yv)
{
    *xw = wcx + (xv - vsx) / scale;
    // data window와 mouse 영역의 y축이 반대임에 주의
    *yw = wcy - (yv - vsy) / scale;
}

/////////////////////////////////////////////////////////////////////////////
// CGL01View diagnostics

#ifdef _DEBUG
void CGL01View::AssertValid() const
{
    CView::AssertValid();
}

void CGL01View::Dump(CDumpContext& dc) const
{
    CView::Dump(dc);
}

CGL01Doc* CGL01View::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CGL01Doc)));
    return (CGL01Doc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CGL01View message handlers

BOOL CGL01View::SetupPixelFormat(PIXELFORMATDESCRIPTOR* pPFD)
{
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW |
		PFD_SUPPORT_OPENGL |	// support OpenGL
        PFD_SUPPORT_GDI,		// support GDI
        PFD_TYPE_RGBA,
        24,
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        16,
        0,
        0,
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };

    int pixelformat;

    PIXELFORMATDESCRIPTOR *pPFD2Use;
    pPFD2Use = (0 == pPFD) ? &pfd : pPFD;

    m_hDC = m_pDC->GetSafeHdc();

    if(0 == (pixelformat = ::ChoosePixelFormat(m_hDC, pPFD2Use)) )
    {
        AfxMessageBox(_T("ChoosePixelFormat failed."));
        return FALSE;
    }
    if(FALSE == ::SetPixelFormat(m_hDC, pixelformat, pPFD2Use) )
    {
        AfxMessageBox(_T("SetPixelFormat failed."));
        return FALSE;
    }
    return TRUE;
}

int CGL01View::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;

    // TODO: Add your specialized creation code here
    m_pDC = new CClientDC(this);
    if(NULL == m_pDC)
    {
        AfxMessageBox(_T("DC not taken."));
        return FALSE;
    }
    if(!SetupPixelFormat())
    {
        AfxMessageBox(_T("SetupPixelFormat failed."));
        return FALSE;
    }
    if(0 == (m_hRC = wglCreateContext(m_hDC)) )
    {
        AfxMessageBox(_T("wglCreateContext failed."));
        return FALSE;
    }
    if(FALSE == wglMakeCurrent(m_hDC, m_hRC) )
    {
        AfxMessageBox(_T("wglMakeCurrent failed."));
        return FALSE;
    }
    return 0;
}

void CGL01View::OnDestroy()
{
    CView::OnDestroy();

    // TODO: Add your message handler code here
    if(FALSE == wglMakeCurrent(0,0) )
    {
        AfxMessageBox(_T("wglMakeCurrent failed."));
    }
    if(m_hRC && (FALSE == wglDeleteContext(m_hRC)) )
    {
        AfxMessageBox(_T("wglDeleteContext failed."));
    }
    if(m_pDC != NULL) delete m_pDC;
}

BOOL CGL01View::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    return TRUE;
}

void CGL01View::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);
    // TODO: Add your message handler code here
    glViewport(0, 0, cx, cy);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    // You may have something to do for the resized window
    vsx = .5f * (float)cx;
    vsy = .5f * (float)cy;
}

void CGL01View::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    if( nElements == 0) return;
    anchor = last = point;
}

void CGL01View::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    float fxw, fyw, sxw, syw;

    if( nElements == 0 || anchor == last) return;

    Mouse2WinMapping (&fxw, &fyw, (float)anchor.x, (float)anchor.y);
    Mouse2WinMapping (&sxw, &syw, (float)last.x, (float)last.y);

    if (fxw > sxw) {
        wsx = (fxw - sxw) / 2.f;
        wcx = sxw + wsx;
    }
    else {
        wsx = (sxw - fxw) / 2.f;
        wcx = fxw + wsx;
    }
    if (fyw > syw) {
        wsy = (fyw - syw) / 2.f;
        wcy = syw + wsy;
    }
    else {
        wsy = (syw - fyw) / 2.f;
        wcy = fyw + wsy;
    }

    if (wsx == 0) { scale = vsy / wsy; }
    else if (wsy == 0) { scale = vsx / wsx; }
    else {
        float x_scale, y_scale;

        x_scale = vsx / wsx;
        y_scale = vsy / wsy;
        if (x_scale < y_scale) scale = x_scale;
        else scale = y_scale;
    }

    Invalidate (FALSE);
}

void CGL01View::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default

    if( nElements == 0) return;
    if((nFlags & MK_LBUTTON)==MK_LBUTTON){
        CClientDC dc(this);

        dc.SetROP2(R2_NOTXORPEN);

        // 기존의 사각형을 지운다.
        dc.MoveTo (anchor);
        dc.LineTo (last.x, anchor.y);
        dc.LineTo (last);
        dc.LineTo (anchor.x, last.y);
        dc.LineTo (anchor);

        last = point;

        // 새로운 사각형을 그린다.
        dc.MoveTo (anchor);
        dc.LineTo (last.x, anchor.y);
        dc.LineTo (last);
        dc.LineTo (anchor.x, last.y);
        dc.LineTo (anchor);
    }
}

void CGL01View::OnFileOpen()
{
    // TODO: Add your command handler code here
	TCHAR    FileName[100] = {'\0'};
	TCHAR    FileTitle[100] = {'\0'};

    // OPENFILENAME fp;
	// memset(&fp, 0, sizeof(fp));                         // use this when fp is OPENFILENAME

	LPOPENFILENAME fp;
	fp = (LPOPENFILENAME)calloc(1, sizeof(*fp));     // use this when fp is LPOPENFILENAME
	fp->lStructSize = sizeof(*fp);
    // fp->hwndOwner = NULL;
    // fp->hInstance = NULL;

    fp->lpstrFilter = _T("DAT File (*.dat)\0*.dat\0OUT File (*.out)\0*.out\0All File (*.*)\0*.*\0\0");
    // fp->lpstrCustomFilter = NULL;
    // fp->nMaxCustFilter = 0;
    fp->nFilterIndex = 1;
    fp->lpstrFile = FileName; // 선택된 파일명이 FileName에 저장
    fp->nMaxFile = 500;
    fp->lpstrFileTitle = FileTitle;
    fp->nMaxFileTitle = 90;
    // fp->lpstrInitialDir = NULL;
    fp->lpstrTitle = _T("Open Graphics File"); // 파일 OPEN BOX의 Title
    fp->Flags = OFN_FILEMUSTEXIST;
    fp->lpstrDefExt = _T("DAT");
    // fp->lCustData = NULL;
    // fp->lpfnHook = NULL;
    // fp->lpTemplateName = NULL;
	// fp->FlagsEx = NULL;

    GetOpenFileName(fp);
    if(FileName[0] == '\0') {
        AfxMessageBox (_T("File not opened!"));
        return;
    }

    if(fp != NULL) free(fp);

    FileRead(FileName);

    Invalidate (FALSE);
}

void CGL01View::OnViewRedraw()
{
    float x_scale, y_scale;

    // TODO: Add your command handler code here

    wsx = old_wsx;
    wsy = old_wsy;
    wcx = old_wcx;
    wcy = old_wcy;

    x_scale = vsx / wsx;
    y_scale = vsy / wsy;
    if (x_scale < y_scale) scale = x_scale * .9f;
    else scale = y_scale * .9f;

    Invalidate (FALSE);
}

void CGL01View::OnUpdateViewRedraw(CCmdUI* pCmdUI)
{
    // TODO: Add your command update UI handler code here
    if(nElements > 0) pCmdUI->Enable(TRUE);
    else pCmdUI->Enable(FALSE);
}

void CGL01View::FileRead(TCHAR *FileName)
{
    int i, cnt=0;
	errno_t err;
	TCHAR str[100] = {L"0"};
    float red, green, blue;
    float min_x = 1.0E6f,  min_y = 1.0E6f;
    float max_x = 1.0E-6f, max_y = 1.0E-6f;
    FILE  *fp;

    if ( (err = _tfopen_s(&fp, FileName, _T("r"))) != NULL) {
        AfxMessageBox(_T("선택한 화일을 열 수 없습니다.!"));
		return;
    }
    while (_fgetts(str, 100, fp) != NULL) {
        if (str[0] == 'C') {
            _stscanf_s (_tcschr((LPCTSTR)str, '/')+1, _T("%f %f %f"), &red, &green, &blue);
        }
        else if (str[0] == 'P') {
            DList[nElements].R = red;
            DList[nElements].G = green;
            DList[nElements].B = blue;

            _stscanf_s (_tcschr((LPCTSTR)str, '/')+1, _T("%d"), &DList[nElements].nNodes);
            DList[nElements].XPos = &XNodes[cnt];
            DList[nElements].YPos = &YNodes[cnt];
            for (i = 0; i < DList[nElements].nNodes; i++) {
                _ftscanf_s (fp, _T("%f %f"), &XNodes[cnt], &YNodes[cnt]);
                if (XNodes[cnt] < min_x) min_x = XNodes[cnt];
                if (YNodes[cnt] < min_y) min_y = YNodes[cnt];
                if (XNodes[cnt] > max_x) max_x = XNodes[cnt];
                if (YNodes[cnt] > max_y) max_y = YNodes[cnt];
                cnt++;
            }
            nElements++;
        }
    }
    old_wsx = wsx = (max_x - min_x) / 2.f;
    old_wsy = wsy = (max_y - min_y) / 2.f;
    old_wcx = wcx = min_x + wsx;
    old_wcy = wcy = min_y + wsy;

    float x_scale, y_scale;

    x_scale = vsx / wsx;
    y_scale = vsy / wsy;
    if (x_scale < y_scale) scale = x_scale * .9f;
    else scale = y_scale * .9f;

	fclose(fp);
}
